import gui.*;
import backend.*;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.text.DefaultEditorKit;
import java.io.IOException;

/**
 * The management frame is where all of the real work for managing RBAC objects
 * is done.  There is a users tab for adding/deleting users from the system and
 * inspecting the roles they're assigned to.  The roles tab allows the admin to
 * to add and delete new roles and subroles, as well as see and edit which users
 * are assigned to the roles and edit the permissions associated with a role.
 */
public class ManagementFrame extends JFrame {
    private JPanel main_panel;
    private JButton user_add_button, user_remove_button, role_add_button,
        role_remove_button;
    private JPanel rolePermPanel, roleUserPanel;
    protected AboutBox aboutBox;
    protected PrefPane prefs;
    static final JMenuBar mainMenuBar = new JMenuBar();
    protected JMenu fileMenu, editMenu;
    protected Action newAction, openAction, closeAction, saveAction,
        saveAsAction, cutAction, copyAction, pasteAction, clearAction,
        prefsAction, quitAction, aboutAction;
    protected JPanel login_panel;
    protected DefaultListModel user_data, role_data;
    protected JComboBox user_list_box, role_list_box;
    protected JList roles_list;
    protected JTable perm_table, user_table, users_table, roles_table;
    protected PermissionsTableModel perm_table_model;
    protected UserTableModel user_table_model;
    protected UserViewTableModel users_view_model;
    protected Server server;
    protected JFrame login_frame;
    protected JTree role_tree;
    protected DefaultMutableTreeNode tree_top;
    protected DefaultTreeModel role_model;
    protected JPanel button_panel;
    protected DefaultListModel users_model;
    protected JComboBox user_editor;

    // Check if we are on Mac OS X.  This is crucial to loading and using the OSXAdapter class.
    public static boolean MAC_OS_X = System.getProperty("os.name").startsWith("Mac OS X");

    // Define sizes of elements for uniformity
    final static Dimension TAB_LIST_SIZE = new Dimension(150,500);
    final static Dimension USER_INFO_PANE_SIZE = new Dimension(250,500);
    final static Dimension ROLE_INFO_PANE_SIZE = new Dimension(500,250);
    final static Dimension APP_SIZE = 
        new Dimension(TAB_LIST_SIZE.width + ROLE_INFO_PANE_SIZE.width,
                      TAB_LIST_SIZE.height);

    /* An overview of the management panel:
     *     0
     *    +----------------------------------------+
     *  0 | Menubar (non-OSX)                      |
     *    +----------------------------------------+
     *  1 | Users / Roles Tab Pane                 |
     *    +----------------------------------------+
     *  2 | Configuration management buttons       |
     *    +----------------------------------------+
     */
    public ManagementFrame(JFrame login, Server s) {
        super();
        this.login_frame = login;
        this.server = s;

        Dimension screen_size = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        Point loc = new Point((screen_size.width - APP_SIZE.width)/2, 
                              (screen_size.height - APP_SIZE.height)/4);
        this.setTitle("FreeRADIUS Configurator");
        this.setMinimumSize(new Dimension(200,200));
        this.setLocation(loc);
        this.createActions();
        this.addMenus();

        // TODO: This can be changed to be a BoxLayout to simplify things
        main_panel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();

        // Create our tab panels
        JPanel users_panel = this.create_users_panel();
        JPanel roles_panel = this.create_roles_panel();

        // Generate tabs pane
        JTabbedPane tabbed_pane = new JTabbedPane();
        tabbed_pane.addTab("Roles", null, roles_panel, "List of Managed Roles");
        tabbed_pane.setMnemonicAt(0, KeyEvent.VK_1); //TODO: Fix these mnemonics
        tabbed_pane.addTab("Users", null, users_panel, "List of Managed Users");
        tabbed_pane.setMnemonicAt(1, KeyEvent.VK_2);

        tabbed_pane.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent event) {
                user_table_model.rebuildTable();
                users_view_model.rebuildTable();
                repaint();
                
            }
        });
        // Add tabs pane to the main panel
//         c.gridy = 0;
//         c.gridx = 0;
//         c.weightx = 1;
//         c.weighty = 1;
//         c.fill = GridBagConstraints.BOTH;
//         main_panel.add(tabbed_pane, c);

        // Generate buttons
        button_panel = create_button_panel();
        
        Container contentPane = this.getContentPane();
        contentPane.add(tabbed_pane, BorderLayout.CENTER);
        contentPane.add(button_panel, BorderLayout.PAGE_END);
        
        // Display our beautiful creation
        this.registerForMacOSXEvents();
//         this.getContentPane().add(main_panel);
        this.pack();
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    /* An overview of the users tab:
     *    0             1
     *   +-------------+--------------------------+
     * 1 | Users       | Info Pane                |
     *   | list        |                          |
     *   |             |                          |
     *   |             |                          |
     *   +-------------+                          |
     * 2 | +/- buttons |                          |
     *   +-------------+--------------------------+
     */
    private JPanel create_users_panel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
        users_view_model = new UserViewTableModel(server);
        users_table = new JTable(users_view_model);
        
        users_table.getSelectionModel().addListSelectionListener(new
        ListSelectionListener() { 
            public void valueChanged(ListSelectionEvent e) {       
                users_model.clear();
                int[] selectedRow = users_table.getSelectedRows();
                
                String username = (String)users_table.getValueAt(selectedRow[0],0);                
                for(Role r : server.getUsers().get(username).getRoles())
                {                    
                    users_model.addElement(r.getName());
                }
            }
        } );
        
        JScrollPane users_pane = new JScrollPane(users_table);
        users_pane.setBorder(BorderFactory.createTitledBorder("All users on the server"));
        users_pane.setAlignmentX(LEFT_ALIGNMENT);
        users_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        users_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        users_pane.setPreferredSize(USER_INFO_PANE_SIZE);
        panel.add(users_pane);
        
        // Create the list of roles for a user
        users_model = new DefaultListModel();
        
        roles_list = new JList(users_model);
        JScrollPane roles_pane = new JScrollPane(roles_list);
        roles_pane.setBorder(BorderFactory.createTitledBorder("Assigned Roles"));
        roles_pane.setPreferredSize(users_pane.getPreferredSize());
        roles_pane.setAlignmentX(LEFT_ALIGNMENT);
        roles_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        roles_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        
        // Set the contents of the roles list
        panel.add(roles_pane);
        return panel;
    }

    /* An overview of the roles tab:
     *    0             1
     *   +-------------+--------------------------+
     * 1 | (Sub)roles  | Permissions (subpanel)   |
     *   | tree        |                          |
     *   |             |                          |
     *   |             +--------------------------+
     *   |             | Users Assigned           |
     *   +-------------+                          +
     * 2 | +/- buttons |                          |
     *   +-------------+--------------------------+
     */
    private JPanel create_roles_panel() {
        JEditorPane roles_info_pane;
        GridBagConstraints c = new GridBagConstraints();
        JPanel roles_panel = new JPanel(new GridBagLayout());
        int row = 0, col = 0;

        // Add the roles tree
        tree_top = new DefaultMutableTreeNode("Roles");
        role_model = new DefaultTreeModel(tree_top);
        role_model.addTreeModelListener(new RoleModelListener());

        role_tree = new JTree(role_model);
        role_tree.addTreeSelectionListener(new RoleSelectListener());
        role_tree.setEditable(false);
        role_tree.setDragEnabled(true);
        role_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        role_tree.setShowsRootHandles(true);
        role_tree.setTransferHandler(new RoleTransferHandler());
        role_tree.setDropMode(DropMode.ON);

        // Create the roles and sub-roles
        for (Role r : server.getRoles().values()) {
            DefaultMutableTreeNode current = new DefaultMutableTreeNode(r.getName());
            tree_top.add(current);
            create_sub_roles(r, current);
        }

        JScrollPane role_tree_pane = new JScrollPane(role_tree);
        role_tree_pane.setAlignmentX(LEFT_ALIGNMENT);
        role_tree_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        role_tree_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        role_tree_pane.setPreferredSize(TAB_LIST_SIZE);

        c.gridy = row;
        c.gridx = col;
        c.weightx = 0.25;
        c.weighty = 0.75;
        c.fill = GridBagConstraints.BOTH;
        roles_panel.add(role_tree_pane, c);
        row += 1;

        // Add buttons subpanel
        JPanel role_tree_controls = new JPanel();
        role_add_button = new JButton(new ImageIcon("resources/list-add.png"));
        role_add_button.addActionListener(new RoleAddListener());
        role_remove_button = new JButton(new ImageIcon("resources/list-remove.png"));
        role_remove_button.addActionListener(new RoleRemoveListener());
        role_tree_controls.add(role_add_button);
        role_tree_controls.add(role_remove_button);

        c.gridy = row; // Buttons go at the bottom of this column
        c.gridx = col;
        c.anchor = GridBagConstraints.CENTER;
        roles_panel.add(role_tree_controls, c);
        row = 0;
        col += 1;

        // Make the permissions and users info panels a subpanel to themselves
        // because otherwise the GridBagLayout screws up the sizing
        JPanel role_info_panel = new JPanel();
        role_info_panel.setLayout(new BoxLayout(role_info_panel, BoxLayout.PAGE_AXIS));

        // Add permissions panel
        rolePermPanel = new JPanel();
        rolePermPanel.setLayout(new BoxLayout(rolePermPanel, BoxLayout.PAGE_AXIS));
        rolePermPanel.setBorder(BorderFactory.createTitledBorder("Permissions"));
        rolePermPanel.setPreferredSize(ROLE_INFO_PANE_SIZE);

        Role selected = null;
        perm_table_model = new PermissionsTableModel(server, selected, false);
        perm_table = new JTable(perm_table_model);
        perm_table.setDefaultEditor(String.class,
                               new PermissionEditor());
                               
        perm_table.addMouseListener(new MouseAdapter() {
                private void show_popup(MouseEvent e) {
                    // We only care if the event is a popup trigger and the table is enabled
                    if (!e.isPopupTrigger() || !perm_table.isEnabled())
                        return;

                    Point p = new Point(e.getX(), e.getY());
                    int row = perm_table.rowAtPoint(p);
                    // Sanity check the row value
                    if (row < 0 || row >= perm_table.getRowCount())
                        return;

                    //perm_table.getCellEditor().cancelCellEditing();
                    JPopupMenu context_menu = create_perms_cmenu(row);
                    if (context_menu != null && context_menu.getComponentCount() > 0)
                        context_menu.show(perm_table, p.x, p.y);
                }
                public void mousePressed(MouseEvent e) {
                    show_popup(e);
                }
                public void mouseReleased(MouseEvent e) {
                    show_popup(e);
                }
            });

        JScrollPane perm_list_pane = new JScrollPane(perm_table);
        perm_list_pane.setAlignmentX(LEFT_ALIGNMENT);
        perm_list_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        perm_list_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        rolePermPanel.add(perm_list_pane);
        role_info_panel.add(rolePermPanel);

        // Add users panel
        roleUserPanel = new JPanel();
        roleUserPanel.setLayout(new BoxLayout(roleUserPanel, BoxLayout.PAGE_AXIS));
        roleUserPanel.setBorder(BorderFactory.createTitledBorder("Users"));
        roleUserPanel.setPreferredSize(ROLE_INFO_PANE_SIZE);

        user_table_model = new UserTableModel(selected, false);
        user_table = new JTable(user_table_model);
        user_table.addMouseListener(new MouseAdapter() {
                private void show_popup(MouseEvent e) {
                    // We only care if the event is a popup trigger and the table is enabled
                    if (!e.isPopupTrigger() || !user_table.isEnabled())
                        return;

                    Point p = new Point(e.getX(), e.getY());
                    int row = user_table.rowAtPoint(p);
                    // Sanity check the row value
                    if (row < 0 || row >= user_table.getRowCount())
                        return;

                    //perm_table.getCellEditor().cancelCellEditing();
                    JPopupMenu context_menu = create_users_cmenu(row);
                    if (context_menu != null && context_menu.getComponentCount() > 0)
                        context_menu.show(user_table, p.x, p.y);
                }
                public void mousePressed(MouseEvent e) {
                    show_popup(e);
                }
                public void mouseReleased(MouseEvent e) {
                    show_popup(e);
                }
            });
            
        user_editor = new JComboBox();
        user_editor.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                user_editor.removeAllItems();
                for (String name : server.getUsers().keySet())
                {
                    user_editor.addItem(name);
                }
            }
            
        });
        
        user_editor.setEditable(true);
        
        TableColumn userColumn = user_table.getColumnModel().getColumn(0);
        userColumn.setCellEditor(new DefaultCellEditor(user_editor));

        JScrollPane user_list_pane = new JScrollPane(user_table);
        user_list_pane.setAlignmentX(LEFT_ALIGNMENT);
        user_list_pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        user_list_pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        roleUserPanel.add(user_list_pane);
        role_info_panel.add(roleUserPanel);

        c.gridy = row;
        c.gridx = col;
        c.gridheight = GridBagConstraints.REMAINDER;
        c.weightx = 1;
        c.weighty = 1;
        c.fill = GridBagConstraints.BOTH;
        roles_panel.add(role_info_panel, c);

        return roles_panel;
    }
    
    // Creates the panel with buttons
    private JPanel create_button_panel() {
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
        panel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
        panel.add(Box.createHorizontalGlue());
        
        
        // Create the buttons
        JButton saveButton = new JButton("Save Configuration");
        saveButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                server.saveAppConfiguration();
            }
        });
        
        JButton verifyButton = new JButton("Verify Configuration");
        verifyButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean valid = false;
                try {
                    valid = server.verifyConfiguration();
                }
                catch (Exception error)
                {
                    
                }
                if (valid)
                {
                    JOptionPane.showMessageDialog(null, "The server's configuration matches local configuration",
                                                  "Success", JOptionPane.INFORMATION_MESSAGE);
                    
                }
                else
                {
                    JOptionPane.showMessageDialog(null, "The server's configuration does not match local configuration",
                                                  "Warning", JOptionPane.ERROR_MESSAGE);
                }
                
            }
        });
        
        JButton pushButton = new JButton("Push Configuration");
        pushButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    server.pushConfiguration();
                }
                catch (IOException error) {
                    System.out.println(error.getStackTrace());
                }
            }
        });
        
        panel.add(Box.createHorizontalGlue());
        panel.add(saveButton);
        panel.add(Box.createRigidArea(new Dimension(10, 0)));
        panel.add(verifyButton);
        panel.add(Box.createRigidArea(new Dimension(10, 0)));
        panel.add(pushButton);
        
        return panel;
    }

    // Checks to see if child is a subrole of parent at any level
    private boolean isSubRole(Role child, Role parent) {
        if (child.getName().compareTo(parent.getName()) == 0)
            return true;

        for (Role subrole : parent.getSubRoles()) {
            if (child.getName().compareTo(subrole.getName()) == 0)
                return true;
            if (isSubRole(child, subrole))
                return true;
        }
        return false;
    }

    private void create_sub_roles(Role r, DefaultMutableTreeNode parent) {
        for (Role subrole : r.getSubRoles()) {
            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(subrole.getName());
            parent.add(newNode);
            create_sub_roles(subrole, newNode);
        }
    }

    private JPopupMenu create_perms_cmenu(final int row) {
        JPopupMenu context_menu = new JPopupMenu();

        JMenuItem delete_item = new JMenuItem();
        delete_item.setText("Delete this permission");
        delete_item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    int model_row = perm_table.convertRowIndexToModel(row);
                    perm_table_model.deleteRow(model_row);
                }
            });
        context_menu.add(delete_item);

        return context_menu;
    }


    private JPopupMenu create_users_cmenu(final int row) {
        JPopupMenu context_menu = new JPopupMenu();

        JMenuItem delete_item = new JMenuItem();
        delete_item.setText("Delete this user");
        delete_item.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    // Get the name of the selected user 
                    int model_row = user_table.convertRowIndexToModel(row);
                    user_table_model.deleteRow(model_row);
                }
            });
        context_menu.add(delete_item);

        return context_menu;
    }

    /**
     * Generic registration with the Mac OS X application menu.  Checks the
     * platform, then attempts to register with the Apple EAWT. See
     * OSXAdapter.java to see how this is done without directly referencing any
     * Apple APIs.
     */
    public void registerForMacOSXEvents() {
        if (!MAC_OS_X) return;

        try {
            // Generate and register the OSXAdapter, passing it a hash of all the methods we wish to
            // use as delegates for various com.apple.eawt.ApplicationListener methods
            OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("quit", (Class[])null));
            OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("about", (Class[])null));
            OSXAdapter.setPreferencesHandler(this, getClass().getDeclaredMethod("preferences", (Class[])null));
        } catch (Exception e) {
            System.err.println("Error while loading the OSXAdapter:");
            e.printStackTrace();
        }
    }

    public void about() {
        aboutBox = new AboutBox();
        aboutBox.setResizable(false);
        aboutBox.setVisible(true);
    }

    public void preferences() {
        prefs = new PrefPane();
        prefs.setResizable(false);
        prefs.setVisible(true);
    }

    public void quit() {
        System.exit(0);
    }

    /**
     * Setup actions with appropriate keyboard shortcuts
     */
    public void createActions() {
        int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();

        // Create actions that can be used by menus, buttons, toolbars, etc.
        newAction = new newActionClass("New Configuration", KeyStroke.getKeyStroke(KeyEvent.VK_N, mask));
        openAction = new openActionClass("Open File", KeyStroke.getKeyStroke(KeyEvent.VK_O, mask) );
        closeAction = new closeActionClass("Close", KeyStroke.getKeyStroke(KeyEvent.VK_W, mask) );
        saveAction = new saveActionClass("Save", KeyStroke.getKeyStroke(KeyEvent.VK_S, mask) );
        saveAsAction = new saveAsActionClass("Save As");
        cutAction = new cutActionClass("Cut", KeyStroke.getKeyStroke(KeyEvent.VK_X, mask) );
        copyAction = new copyActionClass("Copy", KeyStroke.getKeyStroke(KeyEvent.VK_C, mask) );
        pasteAction = new pasteActionClass("Paste", KeyStroke.getKeyStroke(KeyEvent.VK_V, mask) );
        clearAction = new clearActionClass("Clear");
        prefsAction = new prefsActionClass("Preferences", KeyStroke.getKeyStroke(KeyEvent.VK_COMMA, mask) );
        aboutAction = new aboutActionClass("About FReeconf");
        quitAction = new quitActionClass("Quit", KeyStroke.getKeyStroke(KeyEvent.VK_Q, mask) );
    }

    /**
     * Build the menus for our menubar
     */
    public void addMenus() {
        fileMenu = new JMenu("File");
//         fileMenu.add(new JMenuItem(newAction));
//         fileMenu.add(new JMenuItem(openAction));
//         fileMenu.add(new JMenuItem(closeAction));
//         fileMenu.add(new JMenuItem(saveAction));
//         fileMenu.add(new JMenuItem(saveAsAction));
        // These are added by default on OS X, only add on other platforms
        if (!MAC_OS_X) {
//             fileMenu.add(new JMenuItem(prefsAction));
            fileMenu.add(new JMenuItem(aboutAction));
            fileMenu.add(new JMenuItem(quitAction));
        }
        mainMenuBar.add(fileMenu);

        editMenu = new JMenu("Edit");
        //editMenu.addSeparator();
        editMenu.add(new JMenuItem(cutAction));
        editMenu.add(new JMenuItem(copyAction));
        editMenu.add(new JMenuItem(pasteAction));
        editMenu.add(new JMenuItem(clearAction));
        //editMenu.addSeparator();
        mainMenuBar.add(editMenu);

        this.setJMenuBar(mainMenuBar);
    }

    public class newActionClass extends AbstractAction {
        public newActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("New...");
        }
    }

    public class openActionClass extends AbstractAction {
        public openActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("Open...");
            // Use FileDialog instead of JFileChooser here
            // fc = new JFileChooser();
            // int retval = fc.showOpenDialog(null);
        }
    }

    public class closeActionClass extends AbstractAction {
        public closeActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            quit();
        }
    }

    public class saveActionClass extends AbstractAction {
        public saveActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("Save...");
        }
    }

    public class saveAsActionClass extends AbstractAction {
        public saveAsActionClass(String text) {
            super(text);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("Save As...");
        }
    }

    public class cutActionClass extends AbstractAction {
        protected Action cutAction;
        public cutActionClass(String text, KeyStroke shortcut) {
            super(text);
            //Use default cut support
            cutAction = new DefaultEditorKit.CutAction();
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            //Defer to the default cut action
            cutAction.actionPerformed(e);
        }
    }

    public class copyActionClass extends AbstractAction {
        protected Action copyAction;
        public copyActionClass(String text, KeyStroke shortcut) {
            super(text);
            //Use default copy support
            copyAction = new DefaultEditorKit.CopyAction();
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            //Defer to the default copy action
            copyAction.actionPerformed(e);
        }
    }

    public class pasteActionClass extends AbstractAction {
        protected Action pasteAction;
        public pasteActionClass(String text, KeyStroke shortcut) {
            super(text);
            //Use default paste support
            pasteAction = new DefaultEditorKit.PasteAction();
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            //Defer to the default paste action
            pasteAction.actionPerformed(e);
        }
    }

    public class clearActionClass extends AbstractAction {
        public clearActionClass(String text) {
            super(text);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("Clear...");
        }
    }

    public class prefsActionClass extends AbstractAction {
        public prefsActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("Preferences");
            preferences();
        }
    }

    public class quitActionClass extends AbstractAction {
        public quitActionClass(String text, KeyStroke shortcut) {
            super(text);
            putValue(ACCELERATOR_KEY, shortcut);
        }
        public void actionPerformed(ActionEvent e) {
            quit();
        }
    }

    public class aboutActionClass extends AbstractAction {
        public aboutActionClass(String text) {
            super(text);
        }
        public void actionPerformed(ActionEvent e) {
            System.out.println("About FReeconf");
            about();
        }
    }

 
    class RoleAddListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            // Display a dialog asking for the new role's name
            String name = JOptionPane.showInputDialog(null, "Enter the name for the new role:", "New Role", JOptionPane.QUESTION_MESSAGE);
            try {
                server.addRole(name);    
                DefaultMutableTreeNode current = new DefaultMutableTreeNode(name);
                tree_top.add(current);
                role_model.reload();
                System.out.println(current);
                repaint();
            }
            catch (Exception error) {
                JOptionPane.showMessageDialog(null, error.getMessage(), "Error creating role", JOptionPane.ERROR_MESSAGE);
                System.out.println(error.getStackTrace());
                
            }
        }
    }
    class RoleSelectListener implements TreeSelectionListener {
        public void valueChanged(TreeSelectionEvent e) {
            if (role_tree.getSelectionPaths() == null)
            {
                return;
            }
            DefaultMutableTreeNode selectedNode =
                (DefaultMutableTreeNode) role_tree.getSelectionPaths()[0].getLastPathComponent();
            if (selectedNode != null) {
                // Don't allow the user to edit when the head is selected 
                if (selectedNode == tree_top)
                {
                    user_table_model.setEditable(false);
                    perm_table_model.setEditable(false);
                }
                else
                {
                    user_table_model.setEditable(true);
                    perm_table_model.setEditable(true);
                }
                Role selected = server.getRoles().get(selectedNode.toString());
                perm_table_model.setRole(selected);
                user_table_model.setRole(selected);
            }
        }
    }
    class RoleRemoveListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode selectedNode =
            (DefaultMutableTreeNode) role_tree.getSelectionPaths()[0].getLastPathComponent();
            if(selectedNode != null)
            {
                try {
                    Role role = server.getRoles().get(selectedNode.toString());
                    server.removeRole(selectedNode.toString());
                }
                catch(Exception error)
                {
                }
                role_model.removeNodeFromParent(selectedNode);
            }
            // try {
            //     System.out.println("Removing " + role_tree.getSelectedValue());
            //     server.removeRole(role_tree.getSelectedValue().toString());
            //     role_tree.setListData(server.getRoleNames());
            // }
            // catch (Exception ex) {
            //     System.out.println("Something went horribly wrong: " + ex.getMessage());
            // }
        }
    }

    class RoleModelListener implements TreeModelListener {
        public void treeNodesChanged(TreeModelEvent e) {
            DefaultMutableTreeNode node;
            node = (DefaultMutableTreeNode) e.getTreePath().getLastPathComponent();

            // If the event lists children, then the changed node is the child
            // of the node we've already gotten.  Otherwise, the changed node
            // and the specified node are the same.
            int index = e.getChildIndices()[0];
            node = (DefaultMutableTreeNode) node.getChildAt(index);

        }
        public void treeNodesInserted(TreeModelEvent e) {
            DefaultMutableTreeNode node;
            node = (DefaultMutableTreeNode) e.getTreePath().getLastPathComponent();
            int index = e.getChildIndices()[0];
            node = (DefaultMutableTreeNode) node.getChildAt(index);
        }
        public void treeNodesRemoved(TreeModelEvent e) {
        }
        public void treeStructureChanged(TreeModelEvent e) {            
        }
    }

    public class RoleTransferHandler extends TransferHandler {
        public boolean canImport(TransferHandler.TransferSupport support) {
            if (!support.isDataFlavorSupported(DataFlavor.stringFlavor) ||
                !support.isDrop()) {
                return false;
            }

            JTree.DropLocation dropLocation = (JTree.DropLocation)support.getDropLocation();
            return dropLocation.getPath() != null;
        }

        public int getSourceActions(JComponent comp) {
            return COPY_OR_MOVE;
        }

        public Transferable createTransferable(JComponent comp) {
            DefaultMutableTreeNode selected =
                (DefaultMutableTreeNode) role_tree.getSelectionPaths()[0].getLastPathComponent();
            return new StringSelection(selected.toString());
        }

        public boolean importData(TransferHandler.TransferSupport info) {
            // If we can't handle the import, say so
            if (!canImport(info))
            {
                return false;
            }
            
            // Fetch the drop location
            JTree.DropLocation dl = (JTree.DropLocation)info.getDropLocation();

            // Fetch the path and child index from the drop location
            TreePath path = dl.getPath();
            int childIndex = dl.getChildIndex();

            // Fetch the data and bail if this fails
            String data;
            try {
                data = (String)info.getTransferable().getTransferData(DataFlavor.stringFlavor);
            } catch (UnsupportedFlavorException e) {
                return false;
            } catch (IOException e) {
                return false;
            }
            
            // If child index is -1, the drop was on top of the path, so we'll
            // treat it as inserting at the end of that path's list of children
            if (childIndex == -1) {
                childIndex = 0; //role_tree.getModel().getChildCount(path.getLastPathComponent()) - ;
            }

            // Create a new node to represent the data and insert it into the model
            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(data);
//             DefaultMutableTreeNode newNode = (DefaultMutableTreeNode)role_tree.getSelectionPaths()[0].getLastPathComponent();
            // HACK: Prevent inserting to the top of the tree
            if (newNode.toString().trim().compareTo(tree_top.toString().trim()) == 0)
                return false;

            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)path.getLastPathComponent();
            if(path.getPath().length >= 2)
            {
                DefaultMutableTreeNode topParentNode = (DefaultMutableTreeNode)path.getPath()[1];
                
                // Get the corresponding Role objects and insert as a subrole
                Role childRole = server.getRoles().get(newNode.toString());
                Role parentRole = server.getRoles().get(parentNode.toString());
                if (isSubRole(childRole, parentRole))
                {
                    return false;
                }
                
                try {
                    server.addSubRole(parentNode.toString(), newNode.toString());
//                     parentRole.addSubRole(childRole);
                }
                catch (Exception error) {
                    return false;
                }
            }
            else
            {
                // Do something
            }

            // Remove node from top level
//             role_model.removeNodeFromParent(newNode);
            role_model.insertNodeInto(newNode, parentNode, childIndex);
            role_model.nodeStructureChanged(tree_top);
            
            role_model.reload();
            // Make the new node visible and scroll so that it's visible
            role_tree.makeVisible(path.pathByAddingChild(newNode));
            role_tree.scrollRectToVisible(role_tree.getPathBounds(path.pathByAddingChild(newNode)));
            
            return true;
        }
    }
}

